﻿/*
   RxInputManager
   Copyright 2011 Jordan Neumeyer

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
   
   File: RxKeyboard.cs 
 */

using System;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using Microsoft.Xna.Framework.Input;
using RxInputManager.Extensions;
using RxInputManager.Interfaces;
using System.Collections.Generic;

namespace RxInputManager.Devices
{
    public class RxKeyboard : RxDevice<KeyboardState>, IRxKeyboard
    {
        public RxKeyboard() : this(RxInputScheduler.Scheduler) 
        {
            DeviceName = "Keyboard";
        }
        public RxKeyboard(IScheduler scheduler) : base(scheduler)
        {
            DeviceName = "Keyboard";
        }

        protected override KeyboardState CurrentState
        {
            get { return Keyboard.GetState(); }
        }

        public override bool IsAttached
        {
            get { return true; }
        }

        public IObservable<KeyboardState> KeyPressed(Keys state)
        {
            return baseEvent.KeyPressed(state);
        }

        // Get ride of class state by using a local bool variable
        // then make a static method.
        public IObservable<KeyboardState> KeyPressedOnce(Keys state)
        {
            return baseEvent.Where(states => previousState.IsKeyUp(state))
                            .Where(states => states.IsKeyDown(state));
        }

        public IObservable<KeyboardState> KeyHeld(Keys state, int delay = RxInputManager.Constants.DelayTime)
        {
            return baseEvent.KeyHeld(state, delay);
        }

        // Can move this to an extensions method.
        public IObservable<KeyboardState> KeysHeld(HashSet<Keys> keys, int delay = RxInputManager.Constants.DelayTime)
        {
            return baseEvent.KeysHeld(keys, delay);
        }

        // 
        public IObservable<KeyboardState> KeyReleased(Keys state)
        {
            return baseEvent.KeyReleasedHelper(state);
        }

        public IObservable<Keys> KeysPressed(params Keys[] states)
        {
            return baseEvent.SelectMany(state => state.GetPressedKeys().Join(states, f => f, s => s, (o, i) => i));
        }

        // Move as an extension method.
        public IObservable<Keys> KeysPressed(HashSet<Keys> Keys)
        {
            return baseEvent.ToKeysPressed(Keys);
        }

        public IObservable<Keys> Keys
        {
            get { return baseEvent.SelectMany(keys => keys.GetPressedKeys()); }
        }

        public IObservable<KeyboardState> RawState
        {
            get { return baseEvent; }
        }

        protected bool IsChar(Keys key)
        {
            return char.IsLetterOrDigit((char)key);
        }

        protected char KeyToChar(Keys key, bool shift)
        {
            if (shift || char.IsNumber((char)key))
                return (char)key;
            else
                return (char)(((int)key) + 32);       
        }

        public IObservable<char> CharsEntered
        {
            get { return baseEvent.SelectMany(state => state.GetPressedKeys().Where(IsChar),
                                             (state, key) => KeyToChar(key, state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift) ||
                                                                            state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.RightShift)));   }
        }

        public IObservable<char> CharsEnteredUntil(Keys key)
        {
            return baseEvent.TakeWhile(state => state.IsKeyDown(key))
                            .SelectMany(state => state.GetPressedKeys().Where(IsChar),
                                        (state, keyState) => KeyToChar(keyState, state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.LeftShift) ||
                                                                                 state.IsKeyDown(Microsoft.Xna.Framework.Input.Keys.RightShift))
                                       );
        }
    }
}
